package db;

import java.util.*;
import java.io.*;
import java.net.URL;
import org.xml.sax.helpers.DefaultHandler;

import antlr.*;
import antlr.collections.AST;
import antlr.debug.misc.*;
import java.awt.event.*;

import JavaGrammar.*;
import SymbolTable.*;
import FlowGraph.FlowGraph;
import FlowGraph.FlowGraphNode;
import FlowGraph.FlowGraphEdge;
import JavaGrammar.TreeScanners.ParDefParser;
import JavaGrammar.TreeScanners.JavaPrinter;
import FlowGraph.Nodes.*;
import FlowGraph.StatementInfo.*;
import JavaGrammar.TreeScanners.ExceptionScanner;
import FlowGraph.EdgeInfo.*;
import util.*;
import MethodGraph.*;

//////////////////////////////////////////////////////////////////////////
//// ClassData
/**
    Essa classe eh responsavel por armazenar todas as informacoes obtidas
    atraves do parsing do codigo fonte da classe.

@author Ivan Jeukens
@version v 0.1 09/11/2004
*/
public class ClassData extends dbInfo {

    /* Utilizado quando carrego uma classe interna com o load, ou quando
    estou criando uma classe local no InnerClassNode */
    public ClassData(String className, boolean isInner, AST root) {
/*
        _name = className;
        _code = null;
        _isInner = isInner;
        _rootAST = (LinkedAST) root; */
    }

    // Utilizado quando a classe e construida a partir do source
    public ClassData(String className, AST root, String pack, boolean isInner) {
        _name = className;
        _package = pack;
        _isInner = isInner;

        _initialize();

        // Para uma classe interna, os linked ast ja foram criados
        if(!_isInner) {
            _rootAST = new LinkedAST();
            _createLinkedAST(_rootAST, root);
        }
        else {
            _rootAST = (LinkedAST) root;
        }

        if(!_isInner) {
            _readImports();
        }
        _processImplements();
        _processFieldDeclarations();
        _processInnerClasses();
        _buildFlowGraphs();
    }

    // Esse constructor e necessario pois quando crio uma classe no
    // processInnerClasses, so da para setar o imports depois. Mas
    // Alguns metodos do constructor ja precisam dele.
     public ClassData(String className, AST root, String pack, boolean isInner, 
             LinkedList imports) {
        _name = className;
        _package = pack;
        _isInner = isInner;
        _imports = imports;

        _initialize();

        // Para uma classe interna, os linked ast ja foram criados
        if(!_isInner) {
            _rootAST = new LinkedAST();
            _createLinkedAST(_rootAST, root);
        }
        else {
            _rootAST = (LinkedAST) root;
        }

        if(!_isInner) {
            _readImports();
        }

        _processImplements();
        _processFieldDeclarations();
        _processInnerClasses();
        _buildFlowGraphs();
    }

    ///////////////////////////////////////////////////////////////////
    ////                        public methods                     //// 

    public SymbolTable getPorts() {
        return _ports;
    }
    
    public SymbolTable getParameters() {
        return _parameters;
    }
    
    public SymbolTable getVariables() {
        return _variables;
    }
    
    public SymbolTable getMethods() {
        return _methods;
    }
    
    /*
    public void buildAnonymousClass() {
        if(!_isInner || _rootAST.getType() != JavaTokenTypes.OBJBLOCK) return;

        _initialize();

        if(!_isInner) {
            _readImports();
        }
        _processFieldDeclarations();
        _processInnerClasses();
        _buildFlowGraphs();

        //_debugAST();
    }

    public void setImports(LinkedList imports) {
        _imports = imports;
    }
*/
    public LinkedList getImports() {
        return _imports;
    }
    
    public Iterator implementsIterator() {
        return _implements.iterator();
    }
    
/*    

    // **** Metodos dos campos de portos ....
    public void addPort(STPortEntry pe) {
        _ports.putElement(pe.getName(), pe);
    }

    public int portsSize() {
        return _ports.size();
    }
*/

    public STPortEntry getPort(String name) {
        return (STPortEntry) _ports.find(name);
    }

    public Iterator portsIterator() {
        return _ports.iterator();
    }
    
    public Iterator getAllPorts() {
        return _ports.getAllElements().iterator();
    }

    /*
    // ..... metodos dos campos de portos ****
 
    // **** metodos dos campos de parametros ...
    public void addParameter(STParameterEntry p) {
        _parameters.putElement(p.getName(), p);
    }

    public int parameterSize() {
        return _parameters.size();
    }
*/
    public STParameterEntry getParameter(String name) {
        return (STParameterEntry) _parameters.find(name);
    }

    public Iterator parametersIterator() {
        return _parameters.iterator();
    }

    /*

    // **** metodos dos campos de variaveis
    public void addVariable(STVariableEntry p) {
        _variables.putElement(p.getName(), p);
    }
    
    public int variableSize() {
        return _variables.size();
    }
*/

    public STVariableEntry getVariable(String name) {
        return (STVariableEntry) _variables.find(name);
    }

    public Iterator variablesIterator() {
        return _variables.iterator();
    }

/*    
    // *** Metodos para manipular a ST de metodos .....
    public void addMethod(STMethodEntry me) {
        LinkedList l;
        if(_methods.hasElement(me.getName())) {
            l = (LinkedList) _methods.getElement(me.getName());
            l.add(me);
        }
        else {
            l = new LinkedList();
            l.add(me);
            _methods.putElement(me.getName(), l);
        }
    }

    public int methodSize() {
        return _methods.size();
    }
*/

    public LinkedList getMethod(String name) {
        return (LinkedList) _methods.find(name);
    }

    public Iterator methodsIterator() {
        return _methods.iterator();
    }

    /*
    public Iterator methodsIterator() {
        return _methods.iterator();
    }
   // ..... Metodos para manipular a ST de metodos ***

    public void addConstructor(STMethodEntry me) {
        _constructors.add(me);
    }
*/

    public Iterator constructorsIterator() {
        return _constructors.iterator();
    }

/*
    public Object [] getConstructorsArray() {
        return  _constructors.toArray();
    }

    public void addInnerClass(ClassInfo inner) {
        _innerClasses.putElement(inner.getName(), inner);
    }
*/
    public ClassData getInnerClass(String name) {
        return (ClassData) _innerClasses.find(name);
    }

    public void setSuperClass(ClassData superClass) {
        _superClass = superClass;
        if(_superClass != null) {
            _superClass.linkPorts(_ports);
            _superClass.linkParameters(_parameters);
            _superClass.linkMethods(_methods);
            _superClass.linkVariables(_variables);
        } 
    }

    public ClassData getSuperClass() {
        return _superClass;
    }
    
    public void linkPorts(SymbolTable s) {
        s.link(_ports);
    }

    public void linkParameters(SymbolTable s) {
        s.link(_parameters);
    }

    public void linkVariables(SymbolTable s) {
        s.link(_variables);
    }

    public void linkMethods(SymbolTable s) {
        s.link(_methods);
    }

    /*
    public AST getRoot() {
        return _rootAST;
    }

    public AST getDeepFirstASTNode(int index) {
        if(index > _deepFirst.size()) return null;
        return (AST) _deepFirst.get(index);
    }    
    
    public void setCode(LinkedList code) {
        _code = code;
    }
*/

    public String getPackage() {
        return _package;
    }
/*
    public void setPackage(String pack) {
        _package = pack;
    }

    */
    public boolean isInner() {
        return _isInner;
    }
    
    /*
    public void setIsInner(boolean value) {
        _isInner = value;
    }
*/
    public void debug(String basepath) {
        try {
            File f = new File(basepath + "/" + _name + "/");
            f.mkdirs();

            f = new File(basepath + "/" + _name + "/methods");
            f.createNewFile();
            FileOutputStream fo = new FileOutputStream(f);
            DataOutputStream ps = new DataOutputStream(fo);

            // _debugAST();

            Iterator ite = _methods.iterator();
            while(ite.hasNext()) {
                LinkedList l = (LinkedList) ite.next();
                for(int i = 0;i < l.size();i++) {
                    STMethodEntry me = (STMethodEntry) l.get(i);
                    FlowGraph fg = me.getFlowGraph();
                    if(fg.isBuild()) {
                        fg.debug(basepath + "/" + _name + "/", i);
                        ps.writeUTF(
                            new String(fg.getMethodName() + "-v" + i));
                        //fg.dumpSTs();
                    }
                }
            }

            for(int i = 0;i < _constructors.size();i++) {
                STMethodEntry me = (STMethodEntry) _constructors.get(i);
                FlowGraph fg = me.getFlowGraph();
                if(fg.isBuild()) {
                    fg.debug(basepath + "/" + _name + "/", i);
                    ps.writeUTF(
                          new String(fg.getMethodName() + "-v" + i));
                    //fg.dumpSTs();
                }
            }
            fo.close();

            if(_code != null) {
                f = new File(basepath + "/" + _name + "/code");
                f.createNewFile();
                fo = new FileOutputStream(f);
                ps = new DataOutputStream(fo);

                ps.writeUTF(getCodeText());

                fo.close();
            }
        }
        catch(IOException ex) {
            System.out.println(ex);
        }
    }

    public void visit(LinkedList collectors) {
        _ports.visitLocalEntries(collectors, this);
        _parameters.visitLocalEntries(collectors, this);
        _variables.visitLocalEntries(collectors, this);
        // O visitLocalEntries vai descobrir que e uma lista
        // de STMethodEntry, e chamar o visitNodes
        _methods.visitLocalEntries(collectors, this);

        for(int i = 0;i < _constructors.size();i++) {
            STMethodEntry me = (STMethodEntry) _constructors.get(i);
            me.getFlowGraph().visitNodes(collectors);
        }
    }

/*

    // Determina se o nome dado e de uma variavel dessa classe, ou de alguma
    // classe anterior.
    public boolean isVariable(String name) {
        return false;
    }
*/

    // ****************** GERA O CODIGO DA CLASSE ***************************
    public LinkedList getCode() {
        return _code;
    }

    // Source code da classe criado a partir das estruturas
    public String getCodeText() {
        StringBuffer buf = new StringBuffer();
        for(int i = 0;i < _code.size();i++) {
            Line l = (Line) _code.get(i);
            for(int j = 0;j < l.getIdent();j++) {
                buf.append(" ");
            }            
            buf.append(l.getText());
            for(int j = 0;j < l.getSpaces();j++) {
                buf.append("\n");
            }
        }
        return buf.toString();
    }
    
    // So posso gerar o codigo depois de fazer o parse dos MCs
    public void generateCode() {
        // as inner classes primeiro, pois para construir o codigo da classe 
        // principal, eu vou copiar codigo das inner
        Iterator ite = _innerClasses.iterator();
        while(ite.hasNext()) {
            ClassData info = (ClassData) ite.next();
            info.generateCode();
        }
        // if(_codeGenerated) return;

        _code = null;
        _generateCode();
        // _codeGenerated = true;
    }

    // Coxa a geracao do diretorio /home/ijeukens
    public void checkCode() {
        if(_code == null) return;
        
        if(_name.equals("ptolemy.actor.AtomicActor") ||
           _name.equals("ptolemy.actor.TypedAtomicActor") ||
           _name.equals("ptolemy.kernel.util.NamedObj") ||
           _name.equals("ptolemy.kernel.Entity") ||
           _name.equals("ptolemy.kernel.ComponentEntity")) return;

        String text = getCodeText();
        StringTokenizer st = new StringTokenizer(_url.getFile(), "/");
        String tok = null;

        while(st.hasMoreTokens()) {
            tok = st.nextToken();
        }

        Runtime run = Runtime.getRuntime();
        try {
            System.out.println("## GENERATING " + tok);
            File f = new File("/home/ivan/ta/" + tok);
            FileOutputStream fo = new FileOutputStream(f);
            PrintStream ps = new PrintStream(fo);
            ps.print(text);

            //System.out.println("## COMPILING " + tok);
            //Process p = run.exec("javac /home/ivan/ta/" + tok);
            //try {
            //    p.waitFor();
           // }
           // catch(Exception ex) { System.out.println(ex); }
            //InputStream er = p.getErrorStream();
            //int av = er.available();
            //byte [] b = new byte[av];
            //er.read(b);
            //System.out.write(b);
        }
        catch(IOException ex) {
            System.out.println(ex);
        }
        System.out.println("-- COMPILATION DONE --");
    }

    // ********** OPERACOES SOBRE OS FLOW GRAPHS **********************
    
    public void buildMethodFG(String methodName) {
        LinkedList l = (LinkedList) _methods.getElement(methodName);
        if(l == null) return;

        for(int i = 0;i < l.size();i++) {
            STMethodEntry me = (STMethodEntry) l.get(i);
            buildMethodFG(me.getFlowGraph());
        }
    }

    public void buildMethodFG(FlowGraph fg) {
        fg.build();
    }

    
    // Cria recursivamente os flowgraphs de mc nodes. Tem que ser feito
    // depois do link mc node.
    public void buildMCNodesFG() {
        boolean hasNewNode = true;
        HashMap map = new HashMap();

        while(hasNewNode) {
            hasNewNode = false;
            Iterator ite = _methods.getAllElements().iterator();
            while(ite.hasNext()) {
                LinkedList l = (LinkedList) ite.next();
                for(int i = 0;i < l.size();i++) {
                    STMethodEntry me = (STMethodEntry) l.get(i);
                    FlowGraph fg = me.getFlowGraph();
                    if(fg.isBuild() && !map.containsKey(fg)) {
                        map.put(fg, fg);

                        LinkedList lnodes = fg.getAllMCNodes();
                        for(int j = 0;j < lnodes.size();j++) {
                            MethodCallNode mcnode = (MethodCallNode) lnodes.get(j);
                            FlowGraph lfg = mcnode.getFlowGraph();
                            if(lfg != null && !lfg.isBuild()) {
                                lfg.build();
                                performOperations(lfg);
                                hasNewNode = true;
                            }
                        }
                    }
                }
            }
        }
    }

    // Para todos os flow graphs criados para esta classe, efetua operacoes
    // pos-construcao.
    public void performFGOperations() {
        Iterator ite = _methods.getAllElements().iterator();
        while(ite.hasNext()) {
            LinkedList l = (LinkedList) ite.next();
            for(int i = 0;i < l.size();i++) {
                STMethodEntry me = (STMethodEntry) l.get(i);
                FlowGraph fg = me.getFlowGraph();
                if(fg.isBuild() && !fg.opPerformed()) {
                    performOperations(fg);
                }
            }
        }
    }

    public void performOperations(FlowGraph fg) {
        fg.parseMC(fg.getClassInfo());
        fg.addExceptionEdges(fg.getClassInfo());
        fg.linkMCNodes(fg.getClassInfo());
        fg.setOpPerformed();
    }

    public void buildSSA() {
        Iterator ite = _methods.getElements().iterator();
        while(ite.hasNext()) {
            LinkedList l = (LinkedList) ite.next();
            for(int i = 0;i < l.size();i++) {
                STMethodEntry me = (STMethodEntry) l.get(i);
                FlowGraph fg = me.getFlowGraph();
                if(fg.isBuild()) {
                    fg.buildSSA();
                }
            }
        }
    }

    public void markFlowGraphs() {
        Iterator ite = _methods.getElements().iterator();
        while(ite.hasNext()) {
            LinkedList l = (LinkedList) ite.next();
            for(int i = 0;i < l.size();i++) {
                STMethodEntry me = (STMethodEntry) l.get(i);
                FlowGraph fg = me.getFlowGraph();
                if(fg.isBuild()) {
                    fg.markGraph();
                }
            }
        }
    }
    
    public void buildMethodGraphs() {
        Iterator ite = _methods.getElements().iterator();
        while(ite.hasNext()) {
            LinkedList l = (LinkedList) ite.next();
            for(int i = 0;i < l.size();i++) {
                STMethodEntry me = (STMethodEntry) l.get(i);
                FlowGraph fg = me.getFlowGraph();
                if(fg.isBuild()) {
                    fg.buildMethodGraph();
                }
            }
        }
    }

    ///////////////////////////////////////////////////////////////////
    ////                        private methods                    ////

    private void _initialize() {
        if(!_isInner) {
            String s = Util.absolutePath(_name);
            try {
                _url = new URL("file://" + s);
            }
            catch(Exception ex) {
                System.out.println(ex);
            }
        }

        _ports = new SymbolTable();
        _parameters = new SymbolTable();
        _variables = new SymbolTable();
        _methods = new SymbolTable();
        _innerClasses = new SymbolTable();

        _constructors = new LinkedList();
    }

    // Cria a nova arvore de LinkedAST
    private void _createLinkedAST(LinkedAST current, AST old) {
        current.setType(old.getType());
        current.setText(old.getText());
        current.setParent(null);

        LinkedAST sub = _copySubTree(old.getFirstChild());
        if(sub != null) {
            current.setFirstChild(sub);
            sub.setParent(current);
        }
        LinkedAST parent = current;
        AST v = old.getNextSibling();
        while(v != null) {
            current = new LinkedAST();
            current.setType(v.getType());
            current.setText(v.getText());
            current.setParent(parent);
            parent.setNextSibling(current);

            sub = _copySubTree(v.getFirstChild());
            if(sub != null) {
                current.setFirstChild(sub);
                sub.setParent(current);
            }
            parent = current;

            v = v.getNextSibling();
        }
    }

    private LinkedAST _copySubTree(AST v) {
        if(v == null) return null;

        LinkedAST ret = new LinkedAST();
        _createLinkedAST(ret, v);

        return ret;
    }

    // ************** PREAMBULO DA CLASSE - IMPORT E IMPLEMENTS **********
    
    private void _readImports() {
        _imports = new LinkedList();
        AST v = _rootAST;
        while(v.getType() != JavaTokenTypes.CLASS_DEF) {
            if(v.getType() == JavaTokenTypes.IMPORT) {
                AST id = v.getFirstChild();
                NameCollector nc = new NameCollector();
                Util.inorderTraverse(id, nc);
 
                StringTokenizer st = new StringTokenizer(nc.getName(), ".");
                StringBuffer buf = new StringBuffer();
 
                String s = (String) st.nextToken();
                buf.append(s);
                boolean hasStar = false;
                while(st.hasMoreTokens()) {
                    s = (String) st.nextToken();
                    if(!s.equals("*")) {
                        buf.append("." + s);
                    }
                    else {
                        hasStar = true;
                    }
                }

                if(hasStar) buf.append(".");
                _imports.add(buf.toString());
            }
            v = v.getNextSibling();
        }
    }

    private void _processImplements() {
        _implements = new LinkedList();
        AST v = _rootAST;
        while(v.getType() != JavaTokenTypes.CLASS_DEF) {
            v = v.getNextSibling();
        }
        
        if(v == null) return;
        v = v.getFirstChild();
        while(v.getType() != JavaTokenTypes.IMPLEMENTS_CLAUSE) {
            v = v.getNextSibling();
        }
        
        if(v == null) return;
        v = v.getFirstChild();
        while(v != null) {
            NameCollector nc = new NameCollector();
            Util.inorderTraverse(v, nc);

            _implements.add(nc.getName());
            v = v.getNextSibling();
        }
    }

    // ************** CAMPOS DE DADOS DA CLASSE *************************
    
    private void _processFieldDeclarations() {
        AST v = _rootAST;

        // Quando nao for uma classe anonyma ...
        if(v.getType() != JavaTokenTypes.OBJBLOCK) {
            while(v.getType() != JavaTokenTypes.CLASS_DEF) {
                v = v.getNextSibling();
            }
            v = v.getFirstChild();
            v = v.getNextSibling();
            v = v.getNextSibling();
            v = v.getNextSibling();
            v = v.getNextSibling();
        }

        v = v.getFirstChild();
        while(v != null) {
            if(v.getType() == JavaTokenTypes.VARIABLE_DEF) {
                AST vd = v.getFirstChild();
                int mode = STEntry.PACKAGE;
                boolean isStatic = false;
                boolean isFinal = false;

                AST mo = vd.getFirstChild();
                if(mo != null) {
                    do {
                        if(mo.getType() == JavaTokenTypes.LITERAL_public) {
                            mode = STEntry.PUBLIC;
                        }
                        else
                        if(mo.getType() == JavaTokenTypes.LITERAL_private) {
                            mode = STEntry.PRIVATE;
                        }
                        else
                        if(mo.getType() == JavaTokenTypes.LITERAL_protected) {
                            mode = STEntry.PROTECTED;
                        }

                        if(mo.getType() == JavaTokenTypes.LITERAL_static) {
                            isStatic = true;
                        }

                        if(mo.getType() == JavaTokenTypes.FINAL) {
                            isFinal = true;
                        }
                        mo = mo.getNextSibling();
                    } while(mo != null && mo.getType() != JavaTokenTypes.TYPE);
                }

                vd = vd.getNextSibling();
                mo = vd.getFirstChild();
                if(mo.getType() == JavaTokenTypes.ARRAY_DECLARATOR) {
                    AST ar = mo.getFirstChild();
                    int narray = 1;
                    while(ar.getType() == JavaTokenTypes.ARRAY_DECLARATOR) {
                        narray++;
                        ar = ar.getFirstChild();
                    }
                    _processIdent(vd, v, ar, narray, isStatic, isFinal, mode);
                }
                else
                if(mo.getType() == JavaTokenTypes.IDENT ||
                   mo.getType() == JavaTokenTypes.DOT) {
                    _processIdent(vd, v, mo, 0, isStatic, isFinal, mode);
                }
                else
                if(mo.getType() >= JavaTokenTypes.LITERAL_void &&
                   mo.getType() <= JavaTokenTypes.LITERAL_double) {
                     Class type = Util.getType(mo.getText(), _name, _package,
                         _imports);
                     mo = vd.getNextSibling();

                     STVariableEntry var = new STVariableEntry(mo.getText(),
                         mode, v, 0);
                     var.setPrimitive(true);
                     var.setType(type);
                     var.setStatic(isStatic);
                     var.setFinal(isFinal);
                     if(_variables.hasElement(var.getName())) {
                         System.out.println("Error: multiple definition of " +
                            " attribute " + mo.getText());
                     }
                     _variables.putElement(var.getName(), var);
                }
            }
            v = v.getNextSibling();
        }
    }

    private void _processIdent(AST vd, AST v, AST mo, int narray, 
            boolean isStatic, boolean isFinal, int mode) {
        boolean isPort = false;
        boolean isParameter = false;

        NameCollector text = new NameCollector();
        Util.inorderTraverse(mo, text);

        Class type = Util.getType(text.getName(), _name, _package, _imports);
        mo = vd.getNextSibling();

        if(_isIOPort(text.getName())) {
            if(isStatic) {
                System.out.println("Warning : port " + mo.getText() + 
                    " is a static");
            }
            if(mode == STEntry.PRIVATE) {
                System.out.println("Warning : port " +  mo.getText() + 
                    " is private");                          
            }
            isPort = true;
        }
        else
        if(_isParameter(text.getName()))  {
            if(isStatic) {
                System.out.println("Warning : parameter " + mo.getText() + 
                    " is a static");                            
            }
            if(mode == STEntry.PRIVATE) {
                System.out.println("Warning : parameter " + mo.getText() + 
                    " is private");
            }
            isParameter = true;                                                                                                
        }

        if(isPort) {
            STPortEntry p = new STPortEntry(mo.getText(), mode, v, narray);
            p.setType(type);
            if(_ports.hasElement(p.getName())) {
                System.out.println("Error : multiple definition of " + " port " + 
                    mo.getText());
            }
            _ports.putElement(p.getName(), p);
        }
        else
        if(isParameter) {
            STParameterEntry par = new STParameterEntry(mo.getText(), mode, v, 
                narray);
            par.setType(type);
            if(_parameters.hasElement(par.getName())) {
                System.out.println("Error : multiple definition of " +
                    " parameter " + mo.getText());
            }
            _parameters.putElement(par.getName(), par);
        }
        else {
            STVariableEntry var = new STVariableEntry(mo.getText(), mode, v,
                narray);
            var.setPrimitive(false);
            var.setType(type);
            var.setStatic(isStatic);
            var.setFinal(isFinal);
            if(_variables.hasElement(var.getName())) {
                System.out.println("Error: multiple definition of " + 
                    " attribute " + mo.getText());
            }
            _variables.putElement(var.getName(), var);
        }
    }

    private boolean _isIOPort(String name) {
        if(name.equals("IOPort") ||
           name.equals("TypedIOPort") ||
           name.equals("ptolemy.actor.IOPort") ||
           name.equals("ptolemy.actor.TypedIOPort")) {
            return true;
        }
        else {
            return Util.isSubClass(name, _imports, "ptolemy.actor.IOPort");
        }        
    }
    
    private boolean _isParameter(String name) {
        if(name.equals("Parameter") ||           
           name.equals("ptolemy.data.expr.Parameter")) {
            return true;
        }
        else {
            return Util.isSubClass(name, _imports, "ptolemy.data.expr.Parameter");
        }
    }

   // ************ CLASSES INTERNAS ******************************************
    
   // Procura por classes internas, construindo a respectiva estrutura
    private void _processInnerClasses() {
        AST v = _rootAST;
        // Quando a classe nao for anonima
        if(v.getType() != JavaTokenTypes.OBJBLOCK) {
            while(v.getType() != JavaTokenTypes.CLASS_DEF) {
                v = v.getNextSibling();
            }
            v = v.getFirstChild();
            v = v.getNextSibling();
            v = v.getNextSibling();
            v = v.getNextSibling();
            v = v.getNextSibling();
        }
        v = v.getFirstChild();
        while(v != null) {
            if(v.getType() == JavaTokenTypes.CLASS_DEF) {
               AST n = v.getFirstChild();
               n = n.getNextSibling();

               ClassData cinfo = new ClassData(_name + "." + n.getText(), 
                   v, _package, true, _imports);

               cinfo.setSuperClass(this);
               _innerClasses.putElement(cinfo.getName(), cinfo);
            }
            v = v.getNextSibling();
        }
    }

    // ******************** CONSTRUCAO DOS FLOWGRAPHS INICIAIS *************************
    
    // Constroi o FG para cada METHODDEF OU CDEF encontrado na classe 
    private void _buildFlowGraphs() {
        //Util.imports = _imports;
        AST v = _rootAST;

        // Quando a classe nao for anonima
        if(v.getType() != JavaTokenTypes.OBJBLOCK) {
            while(v.getType() != JavaTokenTypes.CLASS_DEF) {
                v = v.getNextSibling();
            }
            v = v.getFirstChild();
            v = v.getNextSibling();
            v = v.getNextSibling();
            v = v.getNextSibling();
            v = v.getNextSibling();
        }

        int count = 0;
        v = v.getFirstChild();
        while(v != null) {
            if(v.getType() == JavaTokenTypes.CTOR_DEF) {
                AST method = v.getFirstChild();

                // Modifiers
                AST mo = method.getFirstChild();
                int mode = STEntry.PACKAGE;
                if(mo != null) {
                    do {
                        if(mo.getType() == JavaTokenTypes.LITERAL_public) {
                            mode = STEntry.PUBLIC;
                        }
                        else 
                        if(mo.getType() == JavaTokenTypes.LITERAL_private) {
                            mode = STEntry.PRIVATE;
                        }
                        else
                        if(mo.getType() == JavaTokenTypes.LITERAL_protected) {
                            mode = STEntry.PROTECTED;
                        }                                                         
                        mo = mo.getNextSibling();                    
                    } while(mo != null && mo.getType() != JavaTokenTypes.TYPE);
                }

                // methodHead: IDENT
                method = method.getNextSibling();

                // methodHead: PARAMETER
                SymbolTable st = _getMethodParST(method);

                String methodName = new String("contructor_" + count++);
                FlowGraph fg = null;
                while(method != null) {
                    if(method.getType() == JavaTokenTypes.SLIST) {
                        fg = new FlowGraph(methodName, method, this, st, true);
                        break;
                    }
                    method = method.getNextSibling();
                }
                STMethodEntry me = new STMethodEntry(methodName, mode, v, fg);
                _constructors.add(me);
            }
            else
            if(v.getType() == JavaTokenTypes.METHOD_DEF) {
                AST method = v.getFirstChild();

                // modifiers
                AST mo = method.getFirstChild();
                int mode = STEntry.PACKAGE;
                if(mo != null) {
                    do {
                        if(mo.getType() == JavaTokenTypes.LITERAL_public) {
                            mode = STEntry.PUBLIC;
                        }
                        else 
                        if(mo.getType() == JavaTokenTypes.LITERAL_private) {
                            mode = STEntry.PRIVATE;
                        }
                        else
                        if(mo.getType() == JavaTokenTypes.LITERAL_protected) {
                            mode = STEntry.PROTECTED;
                        }
                        mo = mo.getNextSibling();
                    } while(mo != null && mo.getType() != JavaTokenTypes.TYPE);
                }

                // typeSpec
                method = method.getNextSibling();

                // methodHead: IDENT
                method = method.getNextSibling();
                // methodHead: PARAMETER
                SymbolTable st = _getMethodParST(method);
                // slist
                String methodName = method.getText();
                FlowGraph fg = null;
                while(method != null) {
                    if(method.getType() == JavaTokenTypes.SLIST) {
                        fg = new FlowGraph(methodName, method, this, st, false);
                        break;
                    }
                    method = method.getNextSibling();
                }

                LinkedList l;
                STMethodEntry me = new STMethodEntry(methodName, mode, v, fg);
                if(_methods.hasElement(me.getName())) {
                    l = (LinkedList) _methods.getElement(me.getName());
                    l.add(me);
                }
                else {
                    l = new LinkedList();
                    l.add(me);
                    _methods.putElement(me.getName(), l);
                }
            }
            v = v.getNextSibling();
        }
    }

    // Retorna uma ST dos parametros desse metodo.
    private SymbolTable _getMethodParST(AST method) {
        AST par = method.getNextSibling();
        SymbolTable methodParameters = new SymbolTable();
        if(par != null && par.getType() == JavaTokenTypes.PARAMETERS) {
            ParDefParser parParser = new ParDefParser();
            par = par.getFirstChild();
            while(par != null) {
                try {
                    parParser.parameterDef(par);
                }
                catch(RecognitionException ex) {
                    System.out.println(ex);
                }

                STLocalVariableEntry entry = new STLocalVariableEntry(
                    parParser.getParName(), STEntry.LOCAL, par, 
                    parParser.getNarray());

                Class type = Util.getType(parParser.getTypeName(),
                    _name, _package, _imports);
                entry.setType(type);
                entry.setPrimitive(Util.isPrimitive(type));

                methodParameters.putElement(entry.getName(), entry);
                par = par.getNextSibling();
            }
        }
        return methodParameters;
    }

    // ****************** GERACAO DE CODIGO **************************
    
    // Produz as linhas de codigo referentes aos FGs. Vai chamar um scanner
    // para imprimir a classe, e chamar a rotida codegen de um FG para 
    // imprimir o FG.
    private void _generateCode() {
        if(_code == null) {
            JavaPrinter tree = new JavaPrinter();
            tree.setCinfo(this);
            try {
                tree.compilationUnit(_rootAST);
                _code = tree.getLines();
            }
            catch(RecognitionException ex) {
                System.out.println(ex);
            }
        }
    }

/*
    // Cria um vetor de todos os nos do AST da classe. Entao posso associar
    //um no a seu index no vetor. Utilizado pelos exportXML(). 
    private void _buildDeepFirstAST() {
        _deepFirst = new LinkedList();
        // int classCount = 0;
        //_visitAST(_rootAST, classCount);
        _visitAST(_rootAST);
    }

    //private void _visitAST(AST n, int classCount) {
    private void _visitAST(AST n) {
        if(n == null) return;
        
        //if(n.getType() == JavaTokenTypes.CLASS_DEF) {
        //    if(classCount == 0) {
        //        classCount++;
        //    }
        //    else {
        //        return;
        //    }
       // }
        
        AST child = n.getFirstChild();
        if(n != null) {
           // _visitAST(child, classCount);
           _visitAST(child);
        }
        _deepFirst.add(n);
        //_visitAST(n.getNextSibling(), classCount);
        _visitAST(n.getNextSibling());
    }

   
    private void _debugAST() {
        ASTFactory factory = new ASTFactory();
        AST r = factory.create(0,"AST ROOT");
	r.setFirstChild(_rootAST);

        final ASTFrame frame = new ASTFrame("Java AST", r);
	frame.setVisible(true);
	frame.addWindowListener(new WindowAdapter() {
            public void windowClosing (WindowEvent e) {
                frame.setVisible(false); // hide the Frame
                frame.dispose();
            }
        });
    }
*/
    ///////////////////////////////////////////////////////////////////
    ////                        private variables                  ////

    private String _package;

    private LinkedAST _rootAST;
    private LinkedList _deepFirst;

    private LinkedList _imports;
    private LinkedList _implements;

    private SymbolTable _ports;
    private SymbolTable _parameters;
    private SymbolTable _variables;
    private SymbolTable _methods;
    private LinkedList _constructors;
    private SymbolTable _innerClasses;

    private LinkedList _code;

    private boolean _codeGenerated;
    private boolean _isInner;
       
    private ClassData _superClass;
}
